# -*- coding: utf-8 -*-
'''
Created on 2013-4-11
@author: cl.lam
'''
import traceback
from datetime import datetime as dt
from sqlalchemy.sql.expression import and_, desc
import flask
from flask import current_app as app
from flask import g, render_template, flash, session, redirect, url_for, request, abort
from flask.blueprints import Blueprint

from sys2do.views import BasicView
from sys2do.util.decorator import templated, loginRequired, allPermission, \
    anyPermission, mypaginate
from sys2do.util.logic_helper import getPermission, getCurrentShopProfile, \
    checkLogin, getCurrentUserID, makeUpdateLog, checkUpdate
from sys2do.model import qry, db, FinNote, FinNoteDtl, FinAccount, SysLog, SysFile
from sys2do.util.wt_helper import MyDateField
from sys2do.constant import FIN_IN, FIN_OUT, MSG_SAVE_SUCC, MESSAGE_INFO, \
    MSG_NOT_ENOUGH_PARAMS, MESSAGE_ERROR, MSG_SERVER_ERROR, FIN_NEW, \
    MSG_RECORD_NOT_EXIST, FIN_APPROVE, MSG_UPDATE_SUCC, MSG_NO_SUCH_ACTION, \
    MSG_DELETE_SUCC, INACTIVE, ACTIVE, MSG_NOT_ALL_PARAMS_OK, LOG_TYPE_CREATE, \
    LOG_TYPE_REJECT, LOG_TYPE_APPROVE, LOG_TYPE_DEL, MSG_NO_PERMISSION, \
    MSG_NO_ID_SUPPLIED, LOG_TYPE_UPDATE
from sys2do.util.common import _gld, _gp, _g, _gl, multiupload
from sys2do.model.interface import getUserID


__all__ = ['bpFin']


bpFin = Blueprint( 'bpFin', __name__, )

@bpFin.before_request
def _b():
    return checkLogin()

class FinanceView( BasicView ):

    template_folder = 'fin'

    @templated( "index.html" )
    @anyPermission( ['FIN_IN_VIEW', 'FIN_OUT_VIEW'] )
    def index( self ):
        if getPermission( 'FIN_IN_VIEW' ) : return redirect( url_for( '.view', action = 'index_in' ) )
        if getPermission( 'FIN_IN_VIEW' ) : return redirect( url_for( '.view', action = 'index_out' ) )


    @templated( "index_in.html" )
    @mypaginate( 'result' )
    @allPermission( ['FIN_IN_VIEW', ] )
    def index_in( self ):
        if request.method == 'POST':
            form = SrhInForm( request.form )
            session[url_for( '.view', action = 'index_in' )] = form
        else:
            form = session.get( url_for( '.view', action = 'index_in' ), SrhInForm( request.form ) )
        form.url = url_for( '.view', action = 'index_in' )

        spobj = getCurrentShopProfile()
        cds = [FinNote.active == ACTIVE, FinNote.shopID == spobj.shopID, FinNote.direction == FIN_IN, ]

        if form.no.data : cds.append( FinNote.no.like( '%%%s%%' % form.no.data ) )
        if form.invoiceNo.data : cds.append( FinNote.invoiceNo.like( '%%%s%%' % form.invoiceNo.data ) )
        if form.issueTo.data : cds.append( FinNote.issueTo.like( '%%%s%%' % form.issueTo.data ) )
        if form.refer.data : cds.append( FinNote.refer.like( '%%%s%%' % form.refer.data ) )
        if form.createTimeFrom.data : cds.append( FinNote.createTime > form.createTimeFrom.data )
        if form.createTimeTo.data : cds.append( FinNote.createTime < form.createTimeTo.data )

        result = FinNote.iall( conditions = cds, order_by = desc( FinNote.createTime ) )
        return {'result' : result, 'form' : form}



    @templated( "index_out.html" )
    @mypaginate( 'result' )
    @allPermission( ['FIN_OUT_VIEW', ] )
    def index_out( self ):
        if request.method == 'POST':
            form = SrhOutForm( request.form )
            session[url_for( '.view', action = 'index_out' )] = form
        else:
            form = session.get( url_for( '.view', action = 'index_out' ), SrhOutForm( request.form ) )
        form.url = url_for( '.view', action = 'index_out' )

        spobj = getCurrentShopProfile()
        cds = [FinNote.active == ACTIVE, FinNote.shopID == spobj.shopID, FinNote.direction == FIN_OUT, ]

        if form.no.data : cds.append( FinNote.no.like( '%%%s%%' % form.no.data ) )
        if form.invoiceNo.data : cds.append( FinNote.invoiceNo.like( '%%%s%%' % form.invoiceNo.data ) )
        if form.issueFrom.data : cds.append( FinNote.issueFrom.like( '%%%s%%' % form.issueFrom.data ) )
        if form.refer.data : cds.append( FinNote.refer.like( '%%%s%%' % form.refer.data ) )
        if form.createTimeFrom.data : cds.append( FinNote.createTime > form.createTimeFrom.data )
        if form.createTimeTo.data : cds.append( FinNote.createTime < form.createTimeTo.data )

        result = FinNote.iall( conditions = cds, order_by = desc( FinNote.createTime ) )
        return {'result' : result, 'form' : form}


    @templated( 'view.html' )
    @anyPermission( ['FIN_IN_VIEW', 'FIN_OUT_VIEW', ] )
    def view( self ):
        fid = _g( 'id' )
        if not id :
            flash( MSG_NOT_ENOUGH_PARAMS, MESSAGE_ERROR )
            return redirect( url_for( '.view' ) )
        obj = FinNote.get( fid )
        if not obj :
            flash( MSG_RECORD_NOT_EXIST, MESSAGE_ERROR )
            return redirect( url_for( '.view' ) )

        spobj = getCurrentShopProfile()
        return {'obj' : obj , 'accounts' : spobj.accounts}



    @anyPermission( ['FIN_IN_EDIT', 'FIN_OUT_EDIT', ] )
    def add( self ):
        t = _g( 't' )
        if t not in [FIN_IN, FIN_OUT]:
            flash( MSG_NO_SUCH_ACTION, MESSAGE_ERROR )
            return redirect( url_for( 'bpRoot.view' ) )

        if t == FIN_IN and getPermission( 'FIN_IN_EDIT' ):
            return render_template( '%s/add_in.html' % self.template_folder )
        elif t == FIN_OUT and getPermission( 'FIN_OUT_EDIT' ):
            return render_template( '%s/add_out.html' % self.template_folder )

        flash( MSG_NO_PERMISSION, MESSAGE_ERROR )
        return redirect( url_for( 'bpRoot.view' ) )



    @anyPermission( ['FIN_IN_EDIT', 'FIN_OUT_EDIT', ] )
    def saveNew( self ):
        params = _gld( 'direction', 'invoiceNo', 'issueTo', 'refer', 'amount', 'createTime', 'remark' )
        if not params['direction'] or params['direction'] not in [FIN_IN, FIN_OUT, ]:
            flash( MSG_NOT_ENOUGH_PARAMS, MESSAGE_ERROR )
            return redirect( url_for( '.view' ) )
        try:
            spobj = getCurrentShopProfile()
            if not params['createTime'] : params['createTime'] = dt.now()
            params['shopID'] = spobj.shopID
            params['status'] = FIN_NEW
            params['attachment'] = multiupload()
            hdr = FinNote.create( params )
            db.add( hdr )
            for k, v in _gp( 'itemName_' ):
                n, did = k.split( "_" )
                if not v : continue

                itemName, desc, price, qty, amount, remark = v, _g( 'desc_%s' % did ), _g( 'price_%s' % did ), _g( 'qty_%s' % did ), _g( 'amount_%s' % did ), _g( 'remark_%s' % did ),

                dtl = FinNoteDtl.create( {'hdr' : hdr , 'itemName' : itemName, 'desc' : desc, 'price' : price,
                                   'qty' : qty, 'amount' : amount, 'remark' : remark} )
                db.add( dtl )
            db.add( SysLog( refClz = hdr.__class__.__name__, type = LOG_TYPE_CREATE, refID = hdr.id, ) )
            db.commit()
            flash( MSG_SAVE_SUCC, MESSAGE_INFO )

        except:
            self._error( traceback.format_exc() )
            db.rollback()
            flash( MSG_SERVER_ERROR, MESSAGE_ERROR )
        if params['direction'] == FIN_IN : url = url_for( '.view', action = 'index_in' )
        elif params['direction'] == FIN_OUT : url = url_for( '.view', action = 'index_out' )
        else : url = url_for( '.view' )
        return redirect( url )


    @anyPermission( ['FIN_IN_EDIT', 'FIN_OUT_EDIT', ] )
    def update( self ):
        fid = _g( 'id' )
        if not fid :
            flash( MSG_NO_ID_SUPPLIED, MESSAGE_ERROR )
            return redirect( url_for( '.view' ) )
        obj = FinNote.get( fid )
        if not obj :
            flash( MSG_RECORD_NOT_EXIST, MESSAGE_ERROR )
            return redirect( url_for( '.view' ) )

        if obj.direction == FIN_IN and not getPermission( 'FIN_IN_EDIT' ):
            flash( MSG_NO_PERMISSION, MESSAGE_ERROR )
            return redirect( url_for( '.view' ) )
        elif obj.direction == FIN_OUT and not getPermission( 'FIN_OUT_EDIT' ):
            flash( MSG_NO_PERMISSION, MESSAGE_ERROR )
            return redirect( url_for( '.view' ) )

        if request.method != 'POST':
            if obj.direction == FIN_IN:
                return render_template( '%s/update_in.html' % self.template_folder, obj = obj )
            else:
                return render_template( '%s/update_out.html' % self.template_folder, obj = obj )
        try:
            oldCopy = obj.serialize()
            extLog = []
            params = _gld( 'invoiceNo', 'issueTo', 'refer', 'amount', 'createTime', 'remark' )
            params['updateById'] = getCurrentUserID()
            params['updateTime'] = dt.now()
            obj.update( params )

            oldaset = set( map( lambda v : unicode( v.id ), obj.attachment ) )
            newaset = set( _g( 'attachment', '' ).split( "|" ) )
            deltedAtm = oldaset.difference( newaset )
            if deltedAtm :
                delAtms = qry( SysFile ).filter( SysFile.id.in_( list( deltedAtm ) ) )
                extLog.extend( [u'删除附件[%s]。' % da.name for da in delAtms] )
            if newaset : obj.attachment = sorted( list( newaset ) ) + multiupload()
            else : obj.attachment = multiupload()

            for d in obj.dtls:
                if not _g( 'olditemName_%s' % d.id ) :
                    extLog.append( u'删除子项目[%s],数量[%s],备注[%s]。' % ( d.itemName, d.qty, d.remark ) )
                    d.active = 1
                oldDtl = d.serialize()
                d.itemName = _g( 'olditemName_%s' % d.id )
                d.desc = _g( 'olddesc_%s' % d.id )
                d.price = _g( 'oldprice_%s' % d.id )
                d.qty = _g( 'oldqty_%s' % d.id )
                d.amount = _g( 'oldamount_%s' % d.id )
                d.remark_ = _g( 'oldremark_%s' % d.id )

                newDtl = d.serialize()
                tmpLog = checkUpdate( oldDtl, newDtl )
                if tmpLog : extLog.extend( [u'修改子项目 [%s]：' % d.itemName, ] + tmpLog )

            for k, v in _gp( 'itemName_' ):
                n, did = k.split( "_" )
                if not v : continue
                itemName, desc, price, qty, amount, remark = v, _g( 'desc_%s' % did ), _g( 'price_%s' % did ), _g( 'qty_%s' % did ), _g( 'amount_%s' % did ), _g( 'remark_%s' % did ),
                dtl = FinNoteDtl.create( {'hdr' : obj , 'itemName' : itemName, 'desc' : desc, 'price' : price,
                                   'qty' : qty, 'amount' : amount, 'remark' : remark} )
                db.add( dtl )

            newCopy = obj.serialize()
            makeUpdateLog( obj, oldCopy, newCopy, extLog )
            db.commit()
            flash( MSG_UPDATE_SUCC, MESSAGE_INFO )
        except:
            self._error( traceback.format_exc() )
            flash( MSG_SERVER_ERROR, MESSAGE_ERROR )
            db.rollback()
        return redirect( url_for( '.view', action = 'view', id = obj.id ) )




    @anyPermission( ['FIN_IN_APPROVE', 'FIN_OUT_APPROVE', ] )
    def app( self ):
        fid = _g( 'id' )
        flag = _g( 'flag' )
        accountID = _gl( 'accountID' )
        if not fid or not flag:
            flash( MSG_NOT_ENOUGH_PARAMS, MESSAGE_ERROR )
            return redirect( url_for( '.view' ) )
        elif flag == unicode( FIN_APPROVE ) and not accountID:
            flash( MSG_NOT_ALL_PARAMS_OK, MESSAGE_ERROR )
            return redirect( url_for( '.view' ) )

        obj = FinNote.get( fid )
        if not obj :
            flash( MSG_RECORD_NOT_EXIST, MESSAGE_ERROR )
            return url_for( '.view' )

        try:
            obj.status = flag
            if flag == unicode( FIN_APPROVE ):
                accountID = accountID[0]
                if obj.direction == FIN_IN:
                    am = obj.amount
                else:
                    am = -1 * obj.amount
                FinAccount.updateAmount( accountID, am )
                db.add( SysLog( refClz = obj.__class__.__name__, type = LOG_TYPE_APPROVE, refID = obj.id, ) )
            else:
                db.add( SysLog( refClz = obj.__class__.__name__, type = LOG_TYPE_REJECT, refID = obj.id, ) )
            db.commit()
            flash( MSG_UPDATE_SUCC, MESSAGE_INFO )
        except:
            db.rollback()
            flash( MSG_SERVER_ERROR, MESSAGE_ERROR )

        if obj.direction == FIN_IN:  return redirect( url_for( '.view', action = "index_in" ) )
        if obj.direction == FIN_OUT:  return redirect( url_for( '.view', action = "index_out" ) )



    @anyPermission( ['FIN_IN_DEL', 'FIN_OUT_DEL', ] )
    def delete( self ):
        fid = _g( 'id' )
        if not fid :
            flash( MSG_NOT_ENOUGH_PARAMS, MESSAGE_ERROR )
            return url_for( '.view' )

        obj = FinNote.get( fid )
        if not obj :
            flash( MSG_RECORD_NOT_EXIST, MESSAGE_ERROR )
            return url_for( '.view' )

        if obj.direction == FIN_IN:  url = url_for( '.view', action = "index_in" )
        elif obj.direction == FIN_OUT:  url = url_for( '.view', action = "index_out" )
        else : url = url_for( '.view' )

        if obj.status != FIN_NEW:
            flash( MSG_NO_SUCH_ACTION, MESSAGE_ERROR )
        else:
            try:
                obj.active = INACTIVE
                db.add( SysLog( refClz = obj.__class__.__name__, type = LOG_TYPE_DEL, refID = obj.id, ) )
                db.commit()
                flash( MSG_DELETE_SUCC, MESSAGE_INFO )
            except:
                self._error( traceback.format_exc() )
                db.rollback()
                flash( MSG_SERVER_ERROR, MESSAGE_ERROR )
        return redirect( url )



    @templated( 'account.html' )
    @anyPermission( ['FIN_IN_VIEW', 'FIN_OUT_VIEW', ] )
    def account( self ):
        spobj = getCurrentShopProfile()
        return {'accounts' : spobj.accounts }


    @templated( 'account_add.html' )
    @loginRequired
    def addAccount( self ):
        if request.method != 'POST': return {}

        try:
            spobj = getCurrentShopProfile()
            params = _gld( 'name', 'bankID', 'code', 'owner', 'currencyID', 'paytypeID', 'amount', 'remark' )
            params['shopProfileID'] = spobj.id
            obj = FinAccount.create( params )
            db.add( obj )
            db.add( SysLog( refClz = obj.__class__.__name__, type = LOG_TYPE_CREATE, refID = obj.id, ) )
            flash( MSG_SAVE_SUCC, MESSAGE_INFO )
            db.commit()
        except:
            self._error( traceback.format_exc() )
            db.rollback()
            flash( MSG_SERVER_ERROR, MESSAGE_ERROR )
        return redirect( url_for( '.view', action = 'account' ) )



    @templated( 'account_update.html' )
    @loginRequired
    def updateAccount( self ):
        aid = _g( 'id' )
        if not aid :
            flash( MSG_NO_ID_SUPPLIED, MESSAGE_ERROR )
            return redirect( url_for( '.view', action = 'account' ) )
        obj = FinAccount.get( aid )
        if not obj:
            flash( MSG_RECORD_NOT_EXIST, MESSAGE_ERROR )
            return redirect( url_for( '.view', action = 'account' ) )

        if request.method != 'POST': return {'obj' : obj}

        try:
            oldCopy = obj.serialize()
            params = _gld( 'name', 'bankID', 'code', 'owner', 'currencyID', 'paytypeID', 'amount', 'remark' )
            obj.update( params )
            newCopy = obj.serialize()
            makeUpdateLog( obj, oldCopy, newCopy )
            db.commit()
            flash( MSG_UPDATE_SUCC, MESSAGE_INFO )
        except:
            db.rollback()
            self._error( traceback.format_exc() )
            flash( MSG_SERVER_ERROR, MESSAGE_ERROR )
        return redirect( url_for( '.view', action = 'account' ) )





    @templated( 'view_log.html' )
    def viewLog( self ):
        id = _g( 'id' )
        if not id : abort( 404 )
        obj = FinNote.get( id )
        if not obj : abort( 404 )
        return {'obj' : obj}




bpFin.add_url_rule( '/', view_func = FinanceView.as_view( 'view' ), defaults = {'action':'index'} )
bpFin.add_url_rule( '/<action>', view_func = FinanceView.as_view( 'view' ) )



#===============================================================================
# form class
#===============================================================================

from wtforms import Form, TextField

class SrhInForm( Form ):
    no = TextField( u'系统编号', )
    invoiceNo = TextField( u'发票号码', )
    issueTo = TextField( u'发票抬头', )
    refer = TextField( u'涉及', )
    creator = TextField( u'经办人', )
    createTimeFrom = MyDateField( u'创建时间(开始)', )
    createTimeTo = MyDateField( u'创建时间(结束)' )


class SrhOutForm( Form ):
    no = TextField( u'系统编号', )
    invoiceNo = TextField( u'发票号码', )
    issueFrom = TextField( u'开发票方', )
    refer = TextField( u'涉及', )
    creator = TextField( u'经办人', )
    createTimeFrom = MyDateField( u'创建时间(开始)', )
    createTimeTo = MyDateField( u'创建时间(结束)' )
